package cn.rengy.tool.excel;

import cn.rengy.tool.core.util._DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
 * 1 HSSFworkbook Excel2003及以前，扩展名为.xls 65536行，256列 因为限制数据行数较少，一般不会发生OOM
 *
 * 2 XSSFworkbook Excel2007以后，扩展名为.xlsx 支持 2^20=1048576行，16384列  数据限制显著提升，但也正因可读写的数据量变大可能导致OOM
 *
 * 3 SXSSFworkbook Excel2007以后，扩展名为.xlsx 大型Excel 用硬盘换内存，保存最新的rows在内存里，之前的rows写入硬盘，被写入硬盘的rows不可访问，想要访问就要指定读进内存数据的大小，以保证所有数据都能读取，因此，此方法适合大型Excel导出，对于导入还是会发生OOM。
 */
@Slf4j
public class ExcelRead {
    private static NumberFormat numberFormat = NumberFormat.getInstance();

    private static int maxCell=150;
    static{
        numberFormat.setGroupingUsed(false);
    }
    public interface RowHandler{
        void handleRow(List<Map<Integer, String>> rowList);

        /**
         * 是否继续读取下一行
         * @return
         */
        default boolean isContinue() {
            return true;
        }

    }

    private static class DefaultRowHandler implements RowHandler {

        @Override
        public void handleRow(List<Map<Integer, String>> rowList) {
            for(Map<Integer, String> row:rowList){
                for(String cellValue:row.values()){
                    System.out.print(cellValue);
                    System.out.print(",");
                }
                System.out.println();
            }
        }
    }
    public static void main(String[] args) {
        char c='　';
        System.out.println(c);
        File file1=new File("C:\\Users\\ren_g\\Desktop\\data1.xlsx");
        try {
            ExcelRead.read(new DefaultRowHandler(),new FileInputStream(file1),  0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //File file2=new File("D:\\国网电力\\文档\\比较2\\电源项目规划前期建设情况统计表（试行）-全口径-汇总(2).xlsx");
       // System.out.println("====================================================");
       // ExcelRead.out(new DefaultRowHandler(),file2, 5, 4,2);
    }
    public static void read(RowHandler rowHandler, InputStream inputStream, int sheetIndex) throws IOException {
        int dataStartRow=0;
        //FileInputStream input= null;
        XSSFWorkbook workbook=null;
        try {
            //input = new FileInputStream(excelFile);
            workbook = new XSSFWorkbook(inputStream);
            XSSFSheet sheet = workbook.getSheetAt(sheetIndex);
            //log.debug("读取SheetName:{}",sheet.getSheetName());
            //最大列数

            //XSSFRow row=sheet.getRow(maxCellRow);
            //XSSFCell headerCell=null;
            //do{
            //    maxCell++;
            //}while (haveNextCell(row, maxCell));
            //System.out.println(maxCell);
            //从第7行开始读取数据
            String cell0Value=null;
            int startRow=dataStartRow;
            List<Map<Integer, String>> rowList=new ArrayList<>();
            XSSFEvaluationWorkbook xSSFEvaluationWorkbook=XSSFEvaluationWorkbook.create(workbook);
            while (haveNextRow(xSSFEvaluationWorkbook,sheet,startRow)){
                XSSFRow row=sheet.getRow(startRow++);
                Map<Integer, String> rowData=new LinkedHashMap<>();
                //System.out.print(startRow);
                //System.out.print("::");
                for(int i=0;i<maxCell;i++){
                    //log.debug("开始读取第{}行,第{}列",startRow+"",i+"");
                    String cellValue=getValue(xSSFEvaluationWorkbook,row,i);
                    //log.debug("第{}行,第{}列={}",startRow+"",i+"",cellValue);
                    rowData.put(i,cellValue);
                    //System.out.print(cellValue);
                    //System.out.print(",");
                }
                rowList.add(rowData);
                //System.out.println();
                //后边改成连续20行没有记录就不读取
            }
            rowHandler.handleRow(rowList);
        } catch (Exception e) {
            throw e;
        }finally{
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String getValue(XSSFEvaluationWorkbook xSSFEvaluationWorkbook,XSSFRow row,int colNum ){
        //log.debug("开始读取第{}行,第{}列",(row.getRowNum()+1)+"",(colNum+1)+"");
        XSSFCell cell = row.getCell(colNum);
        if (cell==null){
            return "";
        }
        String cellValue = "";
        CellType cellType=cell.getCellType();
        if (cellType== CellType.NUMERIC) {
            if(DateUtil.isCellDateFormatted(cell)){
                cellValue=_DateUtils.formatDate(cell.getDateCellValue(),null,"yyyy-MM-dd");
            }else{

                cellValue=numberFormat.format(cell.getNumericCellValue());
            }
        } else if (cellType == CellType.STRING) {
            cellValue = cell.getStringCellValue();
        }else if (cellType == CellType.BLANK) {
            cellValue = "";
        }else if (cellType == CellType.FORMULA) {
            log.error("第{}行，第{}列",row.getRowNum(),colNum);
            cellValue=String.valueOf(cell.getNumericCellValue());
            //cellValue=cell.getStringCellValue();

            //xSSFEvaluationWorkbook
            //cell.getCellFormula()

            //cellValue=numberFormat.format(cell.getNumericCellValue());
        }
//        else if(cellType == CellType.ERROR){
//            cellValue=String.valueOf(cell.getNumericCellValue());
//        }
        else {
            log.error("第{}行，第{}列",row.getRowNum(),colNum);
            throw new RuntimeException("未处理的cell类型"+cellType.toString());
        }
        if(cellValue!=null){
            cellValue=cellValue.trim();
        }
        //log.debug("[{}]的类型：{}",cellValue,cellType.toString());
        return cellValue;
    }


    /**
     * 连续20行没有数据
     * @param sheet
     * @param rowIndex
     * @return
     */
    private static boolean haveNextRow(XSSFEvaluationWorkbook xSSFEvaluationWorkbook,XSSFSheet sheet,int rowIndex){
        boolean hasRow=false;
        //sheet.getLastRowNum();
        //sheet.getPhysicalNumberOfRows();
        for(int i=rowIndex;i<rowIndex+20;i++){
            XSSFRow row=sheet.getRow(i);
            if(row!=null){
                for(int j=0;j<99;j++){//连续99列没有数据
                    //row.getLastCellNum
                    String cell0Value=getValue(xSSFEvaluationWorkbook,row,j);
                    if(StringUtils.isNotBlank(cell0Value)){
                        hasRow=true;
                        break;
                    }
                }
            }
        }
        return hasRow;
    }

    private static boolean haveNextCell(XSSFRow row,int CellIndex){
        XSSFCell cell=row.getCell(CellIndex);
        return cell!=null;
    }

    /**
     * 百万数据读取
     *         <dependency>
     *             <groupId>com.monitorjbl</groupId>
     *             <artifactId>xlsx-streamer</artifactId>
     *             <version>2.2.0</version>
     *         </dependency>
     *
     * @param inputStream
     * @throws Exception
     */
    private static void readExcelV3(InputStream inputStream) throws Exception {
        /*读取文件
         * import com.monitorjbl.xlsx.StreamingReader;
         * InputStream is = new FileInputStream(new File("/path/to/workbook.xlsx"));
         * Workbook workbook = StreamingReader.builder()
         *         .rowCacheSize(100)    // 缓存到内存中的行数(默认是10)
         *         .bufferSize(4096)     // 读取资源时，缓存到内存的字节大小(默认是1024)
         *         .open(is);            // InputStream或者XLSX格式的File(必须)
         *
         *读取sheet、行和单元格
         *
         * for (Sheet sheet : workbook){
                System.out.println(sheet.getSheetName());
                for (Row r : sheet) {
                    for (Cell c : r) {
                        System.out.println(c.getStringCellValue());
                    }
                }
            }

            * Sheet sheet = workbook.getSheet("Sheet名称")
            *
            *
         */
    }
//    private void readExcelV3Test() {
//        FileInputStream fis = null;
//        try {
//            fis = new FileInputStream(new File("/path/to/workbook.xlsx"));
//            Workbook wb = StreamingReader.builder()
//                    .rowCacheSize(100)  //缓存到内存中的行数，默认是10
//                    .bufferSize(4096)  //读取资源时，缓存到内存的字节大小，默认是1024
//                    .open(fis);  //打开资源，必须，可以是InputStream或者是File，注意：只能打开XLSX格式的文件
//            for (Sheet sheet : wb) {
//                // sheet.getLastRowNum()获取最后一个非空行的行下标，比如总行数为n，则返回的为n-1
//                int sumPageNum = (sheet.getLastRowNum() + 1) / 1000 + 1;       //计算分片数
//                for (int i = 1; i <= sumPageNum; i++) {     //读取每片范围内的内容
//                    try {
//                        int minRowNum = 1000 * (i - 1) + 1;    //计算每片读取的行数范围
//                        int maxRowNum = 1000 * i;
//                        for (Row row : sheet) {
//                            if (isRowEmpty(row)) {
//                                continue;
//                            }
//                            Map<String, Object> columnValueMap = new HashMap<>();
//                            if (row.getRowNum() >= minRowNum && row.getRowNum() <= maxRowNum) {//控制每片读取的行
//                                /**
//                                 *   此处读取每行、每个单元格的内容，组装要写入数据库的对象
//                                 */
//                                if (row.getRowNum() == maxRowNum) {//读取到每片最大行数限制，跳出，执行数据写入
//                                    break;
//                                }
//                            }
//                        }
//                        /**
//                         *   此处执行数据写入
//                         */
//                    } catch (Exception e) {
//                        e.printStackTrace();
//                    } finally {
//                        /**
//                         *   清空对象
//                         *   commonTableEntity = null;
//                         *   columnValueList.clear();
//                         */
//                    }
//                }
//            }
//            wb.close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            IOUtils.closeQuietly(fis);
//        }
//    }
    /**
     * 使用SXSSFWorkbook读取，是streaming版本的XSSFWorkbook
     * 保存最新的rows在内存里，之前的rows写入硬盘，被写入硬盘的rows不可访问，想要访问就要指定读进内存数据的大小，以保证所有数据都能读取
     * 不支持公式的求值
     * @param inputStream
     * @throws Exception
     */
    private static void readExcelV2(InputStream inputStream) throws Exception {
        SXSSFWorkbook workbook = new SXSSFWorkbook(new XSSFWorkbook(inputStream));//默认rowAccessWindowSize=100
        int sheetNumber=workbook.getNumberOfSheets();
        log.info("sheetNumber="+sheetNumber);
        for(int i=0;i<sheetNumber;i++){
            /*
             * 直接使用SXSSFWorkbook无法获取数据，因为SXSSFWorkbook只是缓存，
             * 读取内容、行数等时要先调用getXSSFWorkbook。因为所有的初始模板记录存在于XSSFWorkbook中，
             * 需要用sxssfWorkbook.getXSSFWorkbook()方法去获取初始模板的行数据
             */
            //SXSSFSheet sheet=workbook.getSheetAt(i);
            XSSFSheet sheet=workbook.getXSSFWorkbook().getSheetAt(i);
            log.info(sheet.getSheetName());
            //两种获取行数都不准确
            int lastRowIndex =sheet.getLastRowNum();//格式变化也会被读取，从0开始
            sheet.getPhysicalNumberOfRows();//获取有效行数跳过了空行
            System.out.println("lastRowIndex="+lastRowIndex);
            for(int j=2;j<=lastRowIndex;j++){
                //SXSSFRow row=sheet.getRow(j);
                XSSFRow row=sheet.getRow(j);
                //从第二列开始有数据
                //getLastCellNum
                String name=row.getCell(1).getStringCellValue();//设备名称
                System.out.println(name);
            }
        }
        workbook.close();
        inputStream.close();
    }
}
